#include <global.hpp>

#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/Support/Host.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/TargetRegistry.h>
#include <llvm/Support/FileSystem.h>
// #include <llvm/Support/X86TargetParser.h>
// #include <llvm/IR/PassManager.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Transforms/Utils.h>
#include <llvm/Transforms/InstCombine/InstCombine.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/Scalar/GVN.h>
#include <iostream>

// llvm::legacy::FunctionPassManager theFPM(&theModule);
llvm::TargetMachine *TM;

void init_llvm(bool shouldEnableAllPass) {
    using namespace llvm;
    using namespace std;
    string triple = llvm::sys::getProcessTriple();
    LLVMInitializeX86TargetInfo();
    InitializeNativeTarget();
    LLVMInitializeX86TargetMC();
    InitializeNativeTargetAsmParser();
    InitializeNativeTargetAsmPrinter();
    string err;
    auto target = TargetRegistry::lookupTarget(triple, err);
    if (target == nullptr) {
        cerr << err << endl;
        exit(1);
    }

    string CPU = "generic";
    string feature = "";
    TargetOptions opt;
    auto RM = Optional<Reloc::Model>();
    theModule.setTargetTriple(triple);
    theModule.setDataLayout(TM->createDataLayout());
    // cout << triple << endl;
    // cout << TM->createDataLayout().getStringRepresentation() << endl;

    if (shouldEnableAllPass) {
        theFPM.add(createPromoteMemoryToRegisterPass());
        theFPM.add(createInstructionCombiningPass());
        theFPM.add(createReassociatePass());
        theFPM.add(createGVNPass());
        theFPM.add(createCFGSimplificationPass());
    }
    theFPM.doInitialization();
}

void emit_llvm() {
    using namespace std;
    using namespace llvm;
    string filename = "a.o";
    std::error_code EC;
    raw_fd_ostream dest(filename, EC, sys::fs::OF_None);

    if (EC) {
        cerr << "Open file failed: " << EC.message() << endl;
        exit(1);
    }

    legacy::PassManager pass;
    auto fileType = CGFT_ObjectFile;

    if (TM->addPassesToEmitFile(pass, dest, nullptr, fileType)) {
        cerr << "TargetMachine emit a file of this type failed";
        exit(1);
    }

    pass.run(theModule);
    dest.flush();
}
